using System;
using NHibernate;
using NHibernate.Context;

namespace BankInvest.InstrumentEditor.NHibernate
{
    public interface IUnitOfWork : IDisposable
    {
        ISession CurrentSession { get; }

        void Commit();
    }

    public class UnitOfWork : IUnitOfWork
    {
        private readonly ISessionFactory _sessionFactory;
        private ITransaction _transaction;

        public UnitOfWork(ISessionFactory sessionFactory)
        {
            _sessionFactory = sessionFactory;
            
            CreateAndBindSessionToCurrentSessionContext();

            CurrentSession.FlushMode = FlushMode.Commit;

            StartNewTransaction();
        }

        private void CreateAndBindSessionToCurrentSessionContext() 
        {
            var session = _sessionFactory.OpenSession();

            CurrentSessionContext.Bind(session);
        }

        public ISession CurrentSession
        {
            get { return _sessionFactory.GetCurrentSession(); }
        }

        public void Commit()
        {
            if(!_transaction.IsActive)
                throw new InvalidOperationException("No active transaction");

            try
            {
                _transaction.Commit();
            }
            catch
            {
                // quiz: this code isn't covered by the test suite. how should it be tested?
                _transaction.Rollback();
                throw;
            }

            StartNewTransaction();
        }

        private void StartNewTransaction()
        {
            _transaction = CurrentSession.BeginTransaction();
        }

        public void Dispose()
        {
            var session = CurrentSessionContext.Unbind(_sessionFactory);

            session.Close();

            session.Dispose();

            _transaction = null;
        }
    }
}